//
// Created by 邹迪凯 on 2021/7/20.
//

#ifndef LWPT_ENCRYPTEDDIRECTASSIGNMENT_H
#define LWPT_ENCRYPTEDDIRECTASSIGNMENT_H

#include <openssl/aes.h>
#include "assignment.h"

string ENCRYPTION_KEY = "01234567890123456789012345678901";

class EDTAApplicationChunk : public Chunk {
public:
    string cipher;

    EDTAApplicationChunk(string cipher) : cipher(cipher) {}

    long size() const override {
        return cipher.size();
    }
};

class EDTARegisterChunk : public EDTAApplicationChunk {
public:
    EDTARegisterChunk(string cipher) : EDTAApplicationChunk(cipher) {}
};

class EDTAWorker : public Worker {
public:

    EDTAWorker(string id, Location location) : Worker(id, location) {}

    Chunk *apply(Server &server) override {
        string clearText;
        clearText.append(id).append(location.lat).append(to_string(SPLITTER)).append(location.lon);
        string cipher = encrypt(clearText, ENCRYPTION_KEY);
        return new EDTAApplicationChunk(cipher);
    }
};

class EDTAServer : public Server {
public:

    map<string, unordered_map<string, double> *> taskWorkerDistance;

    EDTAServer() {}

    void prepareForAssignment() override {
        vector<Task> tasks;
        vector<PRTAWorker> workers;
        for (const auto &reg : *registrations) {
            auto r = static_cast<EDTARegisterChunk *>(reg);
            //decrypt
            string clearText = decrypt(r->cipher, ENCRYPTION_KEY);
            string id = clearText.substr(0, ID_LENGTH);
            string location = clearText.substr(ID_LENGTH);
            int si = location.find_first_of(SPLITTER);
            Location loc(location.substr(0, si), location.substr(si + 1));
            tasks.emplace_back(id, loc);
        }
        for (const auto &task : tasks) {
            taskWorkerDistance[task.id] = new unordered_map<string, double>;
        }
        //decrypt and decode
        for (const auto &item : *applications) {
            auto ap = static_cast<EDTAApplicationChunk *>(item);
            //decrypt
            string cipher = ap->cipher;
            string clearText = decrypt(cipher, ENCRYPTION_KEY);
            string id = clearText.substr(0, ID_LENGTH);
            string location = clearText.substr(ID_LENGTH);
            int si = location.find_first_of(SPLITTER);
            Location loc(location.substr(0, si), location.substr(si + 1));
            workers.emplace_back(id, loc);
        }
        //计算每个task和每个worker之间的距离
        for (auto &task : tasks) {
            for (auto &worker : workers) {
                (*taskWorkerDistance[task.id])[worker.id] = task.location.distance(worker.location);
                if ((*taskWorkerDistance[task.id])[worker.id] == 0) {
                    cout << " ";
                }
            }
        }
        for (const auto &task : tasks) {
            delete taskWorkerDistance[task.id];
        }
    }

    void assignTask() override {

    }
};

class EDTATest : public Test {
public:
    EDTATest(DataSource *ds) : Test(ds) {
    }

    Worker *workerFactory(string id, Location location) override {
        return new EDTAWorker(id, location);
    }

    Server *serverFactory() override {
        return new EDTAServer();
    }

    Chunk *createTaskRegistration(Task &task) override {
        string clearText;
        clearText.append(task.id).append(task.location.lat).append(to_string(SPLITTER)).append(task.location.lon);
        string cipher = encrypt(clearText, ENCRYPTION_KEY);
        return new EDTARegisterChunk(cipher);
    }

    string name() override {
        return string("EDTA");
    }
};

#endif //LWPT_ENCRYPTEDDIRECTASSIGNMENT_H
